/**
 * User: alesj
 * Date: 2004.8.23
 * Time: 15:42:22
 */

package com.generalynx.common.web.tags;

import com.generalynx.common.constants.WebConst;
import com.generalynx.common.userdata.IUser;
import com.generalynx.common.web.module.Module;
import com.generalynx.common.web.module.Modules;
import com.generalynx.common.web.module.UserModules;
import com.generalynx.common.web.navigation.IMenuItemRenderer;
import com.generalynx.common.userdata.IUser;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.util.RequestUtils;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
import java.io.IOException;
import java.util.*;

public class ModuleMenuTag extends BodyTagSupport {

    private int m_level = -1;
    private String m_renderer;
    private String m_scope = "page";
    private String m_excluded;
    private Set m_sExcluded = null;
    private int m_chosenModuleId = 0;

    private List m_menuItems;
    private int m_iCurrentItem;
    private IMenuItemRenderer m_imir = null;
    private UserModules m_userModules;
    private Module m_parentModule;

    protected final Log logger = LogFactory.getLog(getClass());

    private static final int ITEM_UNDEFINED = 0;
    private static final int ITEM_PERMITTED = 1;
    private static final int ITEM_SELECTED = 2;
    private static final int ITEM_RESTRICTED = 3;

    public int getChosen() {
        return m_chosenModuleId;
    }

    public void setChosen(int chosenModuleId) {
        m_chosenModuleId = chosenModuleId;
    }

    public int getLevel() throws JspException {
        return m_level;
    }

    public void setLevel(int level) {
        m_level = level;
    }

    public String getRenderer() {
        return m_renderer;
    }

    public void setRenderer(String renderer) {
        m_renderer = renderer;
    }

    public String getScope() {
        return m_scope;
    }

    public void setScope(String scope) {
        m_scope = scope;
    }

    public int doStartTag() throws JspException {
        // instantiate renderer
        if (m_renderer == null) {
            throw new JspException("Module renderer cannot be null!");
        }
        try {
            Class clazz = Class.forName(getRenderer());
            m_imir = (IMenuItemRenderer)clazz.newInstance();
        } catch (Exception e) {
            throw new JspException("Cannot instantiate ItemRenderer of class '" + getRenderer() + "'. ", e);
        }

        ModuleMenuTag parentTag = (getParent() instanceof ModuleMenuTag)
                ? (ModuleMenuTag)getParent() : null;

        // check level; get it from the parentTag if needed
        if (m_level < 0) {
            if (parentTag != null) {
                m_level = parentTag.getLevel() + 1;
            }
            else {
                throw new JspException("ModuleMenuTag: no level specified");
            }
        }

        m_userModules = (UserModules)RequestUtils.lookup(pageContext, WebConst.USER_MODULES, "request");
        if (m_userModules == null) {
            return SKIP_BODY;
        }

        m_parentModule = parentTag != null ?
                parentTag.getCurrentItem() :
                m_userModules.getModule(getLevel() - 1);

        m_menuItems = new ArrayList(m_parentModule.getChildren());
        m_iCurrentItem = -1;

        return beforeNextItem(EVAL_BODY_INCLUDE);
    }

    private int beforeNextItem(int evalBodyCode) throws JspException {
        int rc = ITEM_UNDEFINED;
        while (m_iCurrentItem < m_menuItems.size() - 1) {
            if ((rc = processNextItem()) != ITEM_RESTRICTED) {
                break;
            }
        }

        if (m_iCurrentItem < m_menuItems.size() && (rc == ITEM_PERMITTED || rc == ITEM_SELECTED)) {
            return evalBodyCode;
        }
        else {
            return SKIP_BODY;
        }
    }

    private int processNextItem() throws JspException {
        int type = ITEM_RESTRICTED;

        m_iCurrentItem++;

        StringBuffer out = new StringBuffer();
        String html = null;

        Module item = getCurrentItem();

        if (!isExcluded(item.getId())) {
            if (m_userModules.isAllowed(item)) {
                boolean chosen;
                Module selectedNode = null;
                try {
                    if (m_chosenModuleId == 0) {
                        selectedNode = m_userModules.getModule(getLevel());
                    }
                    else {
                        selectedNode = Modules.getModule(m_chosenModuleId);
                    }
                    chosen = selectedNode != null && item.equals(selectedNode);
                } catch (IndexOutOfBoundsException e) {
                    IUser user = (IUser)RequestUtils.lookup(pageContext, WebConst.USER, null, "request");
                    logger.warn(
                            "Showing menu for an allowed module '" +
                            m_parentModule.getName() +
                            "' with no allowed submodules for user '" +
                            user.getUsername() +
                            "'.");
                    chosen = false;
                }
                if (chosen) {
                    html = m_imir.chosenItem(item);
                    type = ITEM_SELECTED;
                } else {
                    html = m_imir.permittedItem(item);
                    type = ITEM_PERMITTED;
                }
            } else {
                html = m_imir.restrictedItem(item);
                type = ITEM_RESTRICTED;
            }
        }

        if (html != null)
            out.append(html);

        try {
            pageContext.getOut().write(out.toString());
        } catch (IOException e) {
            throw new JspException(e);
        }

        return type;
    }

    public int doAfterBody() throws JspException {
        return beforeNextItem(EVAL_BODY_AGAIN);
    }

    private Module getCurrentItem() {
        return (Module)m_menuItems.get(m_iCurrentItem);
    }
    
    public String getExcluded() {
        return m_excluded;
    }

    public void setExcluded(String excluded) {
        m_excluded = excluded;
        m_sExcluded = null;
    }

    private Set getExcludedList() {
        if (m_sExcluded == null && m_excluded != null) {
            m_sExcluded = new TreeSet();
            for (StringTokenizer tokenizer = new StringTokenizer(m_excluded); tokenizer.hasMoreTokens();)
                m_sExcluded.add(new Integer(tokenizer.nextToken().trim()));
        }
        return m_sExcluded;
    }

    private boolean isExcluded(int entityID) {
        Set excludedList = getExcludedList();
        return excludedList != null && excludedList.contains(new Integer(entityID));
    }

}
